// -----------------------------------------------------------------------
//  <copyright file="Repository.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024/12/10 18:32:22</last-date>
// -----------------------------------------------------------------------

using Dapper;
using DaprPlus.Authorization;

using OSharp.Reflection;

using Z.EntityFramework.Plus;


namespace DaprPlus.EntityFrameworkCore;

/// <summary>
/// 通用仓储实现
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class Repository<TEntity> : QueryRepository<TEntity>, IRepository<TEntity> where TEntity : class, IEntity, IAggregateRoot
{
    private readonly DbSet<TEntity> _dbSet;
    private readonly IServiceProvider _provider;

    /// <summary>
    /// 初始化一个<see cref="Repository{TEntity}"/>类型的新实例
    /// </summary>
    public Repository(IServiceProvider provider) : base(provider, false)
    {
        _provider = provider;
        _DbContext.ToMaster();
        _dbSet = _DbContext.Set<TEntity>();
    }

    /// <summary>
    /// 获取 当前实体查询数据集
    /// </summary>
    protected override IQueryable<TEntity> Query => _dbSet;

    /// <summary>
    /// 插入实体
    /// </summary>
    /// <param name="entity">实体对象</param>
    /// <param name="cancellationToken"></param>
    /// <returns>新增的实体</returns>
    public virtual async Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entity, nameof(entity));

        cancellationToken = CancellationToken(cancellationToken);
        entity.SetId().CheckICreatedTime().CheckISoftDeletable();
        CheckDataAuth(DataOperation.Create, entity);
        await _dbSet.AddAsync(entity, cancellationToken);
        await _DbContext.SaveChangesAsync(cancellationToken);
        return entity;
    }

    /// <summary>
    /// 插入多个实体
    /// </summary>
    /// <param name="entities">实体对象集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> InsertAsync(ICollection<TEntity> entities, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entities, nameof(entities));
        if (entities.Count == 0)
        {
            return 0;
        }

        foreach (var entity in entities)
        {
            entity.SetId().CheckICreatedTime().CheckISoftDeletable();
        }
        CheckDataAuth(DataOperation.Create, [..entities]);
        cancellationToken = CancellationToken(cancellationToken);
        await _dbSet.AddRangeAsync(entities, cancellationToken);
        var count = await _DbContext.SaveChangesAsync(cancellationToken);

        return count;
    }

    /// <summary>
    /// 更新实体对象
    /// </summary>
    /// <param name="entity">实体对象</param>
    /// <param name="cancellationToken"></param>
    /// <returns>更新后的实体</returns>
    public virtual async Task<TEntity> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entity, nameof(entity));

        cancellationToken = CancellationToken(cancellationToken);
        CheckDataAuth(DataOperation.Update, entity);
        entity = _dbSet.Update(entity).Entity;
        await _DbContext.SaveChangesAsync(cancellationToken);

        return entity;
    }

    /// <summary>
    /// 更新多个实体对象
    /// </summary>
    /// <param name="entities">实体对象集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> UpdateAsync(ICollection<TEntity> entities, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entities, nameof(entities));
        if (entities.Count == 0)
        {
            return 0;
        }

        cancellationToken = CancellationToken(cancellationToken);
        CheckDataAuth(DataOperation.Update, [.. entities]);
        _dbSet.UpdateRange(entities);
        var count = await _DbContext.SaveChangesAsync(cancellationToken);

        return count;
    }

    /// <summary>
    /// 批量更新实体
    /// </summary>
    /// <param name="specification">查询规约</param>
    /// <param name="updateExpression">属性更新表达式</param>
    /// <param name="cancellationToken">异步取消标识</param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> UpdateBatchAsync(ISpecification<TEntity> specification,
        Expression<Func<TEntity, TEntity>> updateExpression,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));
        Check.NotNull(updateExpression, nameof(updateExpression));

        cancellationToken = CancellationToken(cancellationToken);
        await _provider.GetUnitOfWork().BeginOrUseTransactionAsync(cancellationToken);
        var query = SpecificationEvaluator.Query(Query, specification);
        var count = await query.UpdateAsync(updateExpression, cancellationToken);
        return count;
    }

    /// <summary>
    /// 异步删除指定编号的实体
    /// </summary>
    /// <param name="key">实体编号</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> DeleteAsync<TKey>(TKey key, CancellationToken cancellationToken = default)
    {
        Check.NotNull(key, nameof(key));

        cancellationToken = CancellationToken(cancellationToken);
        var entity = await _dbSet.FindAsync([key], cancellationToken);
        if (entity == null)
        {
            return 0;
        }

        var count = await DeleteAsync(entity, cancellationToken);
        return count;
    }

    /// <summary>
    /// 删除实体对象
    /// </summary>
    /// <param name="entity">实体对象</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entity, nameof(entity));

        cancellationToken = CancellationToken(cancellationToken);
        //_dbSet.Remove(entity);
        DeleteInternal(entity);
        var count = await _DbContext.SaveChangesAsync(cancellationToken);

        return count;
    }

    /// <summary>
    /// 删除实体对象
    /// </summary>
    /// <param name="entities">实体对象集合</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> DeleteAsync(ICollection<TEntity> entities, CancellationToken cancellationToken = default)
    {
        Check.NotNull(entities, nameof(entities));
        if (entities.Count == 0)
        {
            return 0;
        }

        //_dbSet.RemoveRange(entities);
        DeleteInternal([.. entities]);
        var count = await _DbContext.SaveChangesAsync(cancellationToken);
        return count;
    }

    /// <summary>
    /// 删除所有符合查询规约的实体
    /// </summary>
    /// <param name="specification">查询规约</param>
    /// <param name="cancellationToken"></param>
    /// <returns>操作影响的行数</returns>
    public virtual async Task<int> DeleteBatchAsync(ISpecification<TEntity> specification,
        CancellationToken cancellationToken = default)
    {
        Check.NotNull(specification, nameof(specification));

        cancellationToken = CancellationToken(cancellationToken);
        await _provider.GetUnitOfWork().BeginOrUseTransactionAsync(cancellationToken);
        IQueryable<TEntity> query = SpecificationEvaluator.Query(Query, specification);
        int count;
        if (typeof(ISoftDeletable).IsAssignableFrom(typeof(TEntity)))
        {
            //逻辑删除
            var now = DateTime.UtcNow;
            var parameter = Expression.Parameter(typeof(TEntity), "entity");
            var property = Expression.Property(parameter, "DeletedTime");
            Expression.Assign(property, Expression.Constant(now));
            var bindings = new List<MemberBinding>
            {
                Expression.Bind(typeof(TEntity).GetProperty("DeletedTime")!, Expression.Constant(now))
            };
            var lambda = Expression.Lambda<Func<TEntity, TEntity>>(
                Expression.MemberInit(Expression.New(typeof(TEntity)), bindings),
                parameter
            );
            count = await query.UpdateAsync(lambda, cancellationToken);
        }
        else
        {
            //物理删除
            count = await query.ExecuteDeleteAsync(cancellationToken);
        }

        return count;
    }

    /// <summary>
    /// 执行指定的SQL语句
    /// </summary>
    /// <param name="sql">执行的SQL语句</param>
    /// <param name="param">SQL参数</param>
    /// <returns></returns>
    public async Task<int> ExecuteSqlCommandAsync(string sql, object? param = null)
    {
        var connection = _DbContext.Database.GetDbConnection();
        if (connection.State != ConnectionState.Open)
        {
            await connection.OpenAsync();
        }

        var count = await connection.ExecuteAsync(sql, param);
        return count;
    }

    /// <summary>
    /// 执行指定的格式化SQL语句
    /// </summary>
    /// <param name="sql">要执行的格式化SQL语句</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async Task<int> ExecuteSqlCommandAsync(FormattableString sql, CancellationToken cancellationToken = default)
    {
        cancellationToken = CancellationToken(cancellationToken);
        var count = await _DbContext.Database.ExecuteSqlAsync(sql, cancellationToken);
        return count;
    }

    private void DeleteInternal(params TEntity[] entities)
    {
        CheckDataAuth(DataOperation.Delete, entities);
        if (typeof(ISoftDeletable).IsAssignableFrom(typeof(TEntity)))
        {
            //逻辑删除
            foreach (var entity in entities)
            {
                ISoftDeletable deletable = (ISoftDeletable)entity;
                deletable.DeletedTime = DateTime.Now;
            }
        }
        else
        {
            //物理删除
            _dbSet.RemoveRange(entities);
        }
    }

    protected virtual void CheckDataAuth(DataOperation dataOperation, params TEntity[] entities)
    {
        var dataAuthService = _provider.GetRequiredService<IDataAuthorizationService>();
        var flag = dataAuthService.CheckEntityAccess(dataOperation, entities);
        if (!flag)
        {
            throw new AuthorizationException(new AuthorizationResult(AuthorizationStatus.Forbidden, $"【{typeof(TEntity).GetDescription()}】的数据权限验证失败"));
        }
    }
}
